/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     licenses@blazegraph.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 * Created on Mar 22, 2007
 */

package com.bigdata.service;

import java.io.IOException;
import java.rmi.Remote;
import java.util.concurrent.ExecutionException;

import com.bigdata.btree.ITuple;
import com.bigdata.journal.ITransactionService;
import com.bigdata.journal.ValidationError;

/**
 * Remote interface by which the {@link ITransactionService} manages the state
 * of transactions on the distributed {@link IDataService}s.
 * 
 * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
 * @version $Id$
 */
public interface ITxCommitProtocol extends Remote {

    /**
     * Notify a data service that it MAY release data required to support views
     * for up to the specified <i>releaseTime </i>. This is the mechanism by
     * which read locks are released. In effect, a read lock is a requirement
     * that the releaseTime not be advanced as far as the start time of the
     * transaction holding that read lock. Periodically and as transactions
     * complete, the transaction manager will advance the releaseTime, thereby
     * releasing read locks.
     * 
     * @param releaseTime
     *            The new release time (strictly advanced by the transaction
     *            manager).
     * 
     * @throws IllegalStateException
     *             if the read lock is set to a time earlier than its current
     *             value.
     * @throws IOException
     *             if there is an RMI problem.
     */
    public void setReleaseTime(long releaseTime) throws IOException;

    /**
     * Request abort of the transaction by the data service. This message is
     * sent in response to {@link ITransactionService#abort(long)} to each
     * {@link IDataService} on which the transaction has written. It is NOT sent
     * for read-only transactions since they have no local state on the
     * {@link IDataService}s.
     * 
     * @param tx
     *            The transaction identifier.
     * 
     * @throws IllegalArgumentException
     *             if the transaction has not been started on this data service.
     * @throws IOException
     *             if there is an RMI problem.
     */
    public void abort(long tx) throws IOException;

    /**
     * Request commit of the transaction by the data service. In the case where
     * the transaction is entirely contained on the data service this method may
     * be used to both prepare (validate) and commit the transaction (a single
     * phase commit). Otherwise a 2-/3- phase commit is required and a separate
     * {@link #prepare(long)} message MUST be used.
     * 
     * @param tx
     *            The transaction identifier.
     * 
     * @return The commit time assigned to that transaction.
     * 
     * @throws IllegalArgumentException
     *             if the transaction is read-only.
     * @throws IllegalStateException
     *             if the transaction is not known to the data service.
     * @throws InterruptedException
     *             if interrupted.
     * @throws ExecutionException
     *             This will wrap a {@link ValidationError} if validation fails.
     * @throws IOException
     *             if there is an RMI problem.
     */
    public long singlePhaseCommit(long tx) throws InterruptedException,
            ExecutionException, IOException;

    /**
     * Request that the {@link IDataService} participate in a 3-phase commit.
     * <p>
     * When the {@link IDataService} is sent the {@link #prepare(long, long)}
     * message it executes a task which will handle commit processing for the
     * transaction. That task MUST hold exclusive locks for the unisolated
     * indices to which the transaction write sets will be applied. While
     * holding those locks, the task must first validate the transaction's write
     * set and then merge down the write set onto the corresponding unisolated
     * indices using the specified <i>revisionTime</i> and checkpoint the
     * indices in order to reduce all possible sources of latency. Note that
     * each {@link IDataService} is able to independently prepare exactly those
     * parts of the transaction's write set which are mapped onto index
     * partitions hosted by a given {@link IDataService}.
     * <p>
     * Once validation is complete and all possible steps have been taken to
     * reduce sources of latency (e.g., checkpoint the indices and pre-extending
     * the store if necessary), the task notifies the
     * {@link ITransactionService} that it has prepared using
     * {@link ITransactionService#prepared(long)}. The
     * {@link ITransactionService} will wait until all tasks have prepared. If a
     * task CAN NOT prepare the transaction, then it MUST throw an exception out
     * of its {@link #prepare(long, long)} method.
     * <p>
     * Once all tasks have send an {@link ITransactionService#prepared(long)}
     * message to the {@link ITransactionService}, it will assign a commitTime
     * to the transaction and permit those methods to return that commitTime to
     * the {@link IDataService}s. Once the task receives the assigned commit
     * time, it must obtain an exclusive write lock for the live journal (this
     * is a higher requirement than just an exclusive lock on the necessary
     * indices and will lock out all other write requests for the journal),
     * register the checkpointed indices on the commit list and then request a
     * commit of the journal using the specified commitTime. The task then
     * notifies the transaction service that it has completed its commit using
     * {@link ITransactionService#committed(long)} and awaits a response. If the
     * {@link ITransactionService} indicates that the commit was not successful,
     * the task rolls back the live journal to the prior commit point and throws
     * an exception out of {@link #prepare(long, long)}.
     * <p>
     * A sample flow for successful a distributed transaction commit is shown
     * below. This example shows two {@link IDataService}s on which the client
     * has written. (If the client only writes on a single data service then we
     * use a single-phase commit protocol).
     * 
     * <pre>
     * client -------+----txService----+--dataService1--+--dataService2--+...           
     *   | [1]                    
     *   | commit(tx) -------- + [2]
     *   |                     | prepare(tx,rev) +
     *   |                     | [3]             |
     *   |                     | prepare(tx,rev) ------------------+
     *   |                     |                 |                 |
     *   |                     | &lt;--prepared(tx) +                 |
     *   |                     |                                   |
     *   |                     | &lt;------------------- prepared(tx) +
     *   |                     |  
     *   |       "prepared" barrier [4]
     *   |                     |
     *   |                     | -- (commitTime) +  
     *   |                     | -------------------- (commitTime) +
     *   |                     | [5]             |                 |
     *   |                     | &lt;--committed(tx)------------------+  
     *   |                     | [6]             |
     *   |                     | &lt;--committed(tx)+
     *   |                     | 
     *   |       "committed" barrier [7]
     *   |                     | [8]
     *   |                     | ------ (success)+  
     *   |                     | [9]             |
     *   |                     | (void)----------+
     *   |                     |                 halt
     *   |                     | [10]             
     *   |                     | ------------------------ (success)+
     *   |                     | [11]                              |
     *   |                     | (void)----------------------------+
     *   |                [12] |                                   halt
     *   | (commitTime)--------+  
     *   |                   
     * </pre>
     * 
     * <ul>
     * <li> [1] The client issues an {@link ITransactionService#commit(long)}
     * request, in which it specifies the transaction identifier (tx). </li>
     * <li> [2,3] The transaction service issues concurrent
     * {@link #prepare(long, long)} requests to the participating
     * {@link IDataService}s, specifying the transaction identifier (tx) and
     * the revision timestamp (rev) to be used and then waits at a barrier until
     * it receives {@link ITransactionService#prepared(long)} messages from
     * those {@link IDataService}s.</li>
     * <li> [4] When all participants have prepared, the barrier breaks and the
     * {@link ITransactionService} assigns a <i>commitTime</i> and returns that
     * commitTime as the return value for the prepared messages.</li>
     * <li> [5,6] Once the {@link IDataService} obtains that commitTime, it
     * proceeds with its atomic commit using the specified commitTime and then
     * sends an {@link ITransactionService#committed(long)} message to the
     * {@link ITransactionService}.</li>
     * <li> [7] The {@link ITransactionService} waits at another barrier.</li>
     * <li> [8,10] Once it has received an
     * {@link ITransactionService#committed(long)} message from each
     * participating {@link IDataService} the transaction has been successfully
     * committed and the barrier breaks. The {@link ITransactionService} now
     * lets the {@link ITransactionService#committed(long)} messages return
     * <code>true</code>, indicating success.</li>
     * <li> [9,11] The {@link IDataService}s return (void) from their
     * {@link #prepare(long, long)} message and the threads running their side
     * of the commit protocol halt.</li>
     * <li> [12] The {@link ITransactionService} returns the commit time which
     * it assigned and which was used by each participating {@link IDataService}
     * to the client.
     * </ul>
     * There are many points in the protocol where commit processing can fail.
     * However, there are two primary failure classifications that are of
     * interest for error handling. Up until the first barrier is satisified,
     * there is no side-effect on the persistent state so error handling need
     * only halt processing on the {@link IDataService}s and discard any local
     * state associated with the transaction and throw an exception out of
     * {@link #prepare(long, long)}. Once the first barrier has been
     * satisfied, persistent side-effects MAY occur. Error handling in this
     * case must rollback the state of the live journal for each of the
     * participating {@link IDataService}s. If error handling was performed in
     * response to a local error, then the {@link IDataService} must throw that
     * error out of {@link #prepare(long, long)}. However, if error handling
     * was initiated because {@link ITransactionService#committed(long)}
     * returned <code>false</code> then it should return normally (after
     * rolling back the journal).
     * 
     * @param tx
     *            The transaction identifier.
     * @param revisionTime
     *            The timestamp that will be written into the {@link ITuple}s
     *            when the write set of the validated transaction is merged down
     *            onto the unisolated indices.
     * 
     * @throws Throwable
     *             if there is a problem during the execution of the commit
     *             protocol by the {@link IDataService}.
     * @throws IOException
     *             if there is an RMI problem.
     * 
     * @todo it may be possible to set the desired commit time on the abstract
     *       task (or a subclass specific to the distributed commit protocol)
     *       and then use that timestamp rather than requesting one from the
     *       {@link ITransactionService} in the group commit. This would allow
     *       us to use the normal commit processing.
     *       <p>
     *       If each distributed transaction gets its own commit time then we
     *       can not allow more than one distributed transaction into a given
     *       commit group. Therefore it seems that the
     *       {@link ITransactionService} would have to be able to assign the
     *       same commitTime to a set of distributed transactions that it knew
     *       were prepared together and would commit together. I can't quite see
     *       how that would work.
     *       <p>
     *       Failing that, we will need to exclude other tasks (or at least
     *       other distributed commit processing tasks) from the commit group.
     */
    public void prepare(long tx, long revisionTime) throws Throwable, IOException;

//    /**
//     * Cancel a 2-phase commit.
//     * 
//     * @param tx
//     *            The transaction identifier.
//     *            
//     * @throws IllegalArgumentException
//     *             if the transaction has not been started on this data service.
//     * @throws IllegalStateException
//     *             if the transaction is not participating in a 2-phase commit.
//     * @throws IOException
//     *             if there is an RMI problem.
//     */
//    public void twoPhaseCancel(long tx) throws IOException;
    
}
